home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / gauss_iir.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-12  |  23.8 KB  |  910 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24.  
  25. #include <gtk/gtk.h>
  26.  
  27. #include <libgimp/gimp.h>
  28. #include <libgimp/gimpui.h>
  29.  
  30. #include "libgimp/stdplugins-intl.h"
  31.  
  32.  
  33. typedef struct
  34. {
  35.   gdouble  radius;
  36.   gboolean horizontal;
  37.   gboolean vertical;
  38. } BlurValues;
  39.  
  40. typedef struct
  41. {
  42.   gdouble horizontal;
  43.   gdouble vertical;
  44. } Blur2Values;
  45.  
  46. typedef struct
  47. {
  48.   GtkWidget *size;
  49.   gint       run;
  50. } BlurInterface;
  51.  
  52.  
  53. /* Declare local functions.
  54.  */
  55. static void      query  (void);
  56. static void      run    (gchar      *name,
  57.              gint        nparams,
  58.              GimpParam  *param,
  59.              gint       *nreturn_vals,
  60.              GimpParam **return_vals);
  61.  
  62. static void      gauss_iir         (GimpDrawable *drawable,
  63.                     gdouble       horizontal,
  64.                     gdouble       vertical);
  65.  
  66. /*
  67.  * Gaussian blur interface
  68.  */
  69. static gint      gauss_iir_dialog  (void);
  70. static gint      gauss_iir2_dialog (gint32        image_ID, 
  71.                     GimpDrawable *drawable);
  72.  
  73. /*
  74.  * Gaussian blur helper functions
  75.  */
  76. static void      find_constants    (gdouble  n_p[],
  77.                     gdouble  n_m[],
  78.                     gdouble  d_p[],
  79.                     gdouble  d_m[],
  80.                     gdouble  bd_p[],
  81.                     gdouble  bd_m[],
  82.                     gdouble  std_dev);
  83. static void      transfer_pixels   (gdouble *src1,
  84.                     gdouble *src2,
  85.                     guchar  *dest,
  86.                     gint     bytes,
  87.                     gint     width);
  88.  
  89. static void      gauss_ok_callback (GtkWidget *widget,
  90.                     gpointer   data);
  91.  
  92. GimpPlugInInfo PLUG_IN_INFO =
  93. {
  94.   NULL,  /* init_proc  */
  95.   NULL,  /* quit_proc  */
  96.   query, /* query_proc */
  97.   run,   /* run_proc   */
  98. };
  99.  
  100. static BlurValues bvals =
  101. {
  102.   5.0,  /*  radius           */
  103.   TRUE, /*  horizontal blur  */
  104.   TRUE  /*  vertical blur    */
  105. };
  106.  
  107. static Blur2Values b2vals =
  108. {
  109.   5.0,  /*  x radius  */
  110.   5.0   /*  y radius  */
  111. };
  112.  
  113. static BlurInterface bint =
  114. {
  115.   FALSE  /*  run  */
  116. };
  117.  
  118.  
  119. MAIN ()
  120.  
  121. static void
  122. query (void)
  123. {
  124.   static GimpParamDef args[] =
  125.   {
  126.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  127.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  128.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  129.     { GIMP_PDB_FLOAT, "radius", "Radius of gaussian blur (in pixels > 1.0)" },
  130.     { GIMP_PDB_INT32, "horizontal", "Blur in horizontal direction" },
  131.     { GIMP_PDB_INT32, "vertical", "Blur in vertical direction" }
  132.   };
  133.   static gint nargs = sizeof (args) / sizeof (args[0]);
  134.  
  135.   static GimpParamDef args2[] =
  136.   {
  137.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  138.     { GIMP_PDB_IMAGE, "image", "Input image" },
  139.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  140.     { GIMP_PDB_FLOAT, "horizontal", "Horizontal radius of gaussian blur (in pixels)" },
  141.     { GIMP_PDB_FLOAT, "vertical",   "Vertical radius of gaussian blur (in pixels)" }
  142.   };
  143.   static gint nargs2 = sizeof (args2) / sizeof (args2[0]);
  144.  
  145.   gimp_install_procedure ("plug_in_gauss_iir",
  146.               "Applies a gaussian blur to the specified drawable.",
  147.               "Applies a gaussian blur to the drawable, with "
  148.               "specified radius of affect.  The standard deviation "
  149.               "of the normal distribution used to modify pixel "
  150.               "values is calculated based on the supplied radius.  "
  151.               "Horizontal and vertical blurring can be "
  152.               "independently invoked by specifying only one to "
  153.               "run.  The IIR gaussian blurring works best for "
  154.               "large radius values and for images which are not "
  155.               "computer-generated.  Values for radius less than "
  156.               "1.0 are invalid as they will generate spurious "
  157.               "results.",
  158.               "Spencer Kimball & Peter Mattis",
  159.               "Spencer Kimball & Peter Mattis",
  160.               "1995-1996",
  161.               NULL,
  162.               "RGB*, GRAY*",
  163.               GIMP_PLUGIN,
  164.               nargs, 0,
  165.               args, NULL);
  166.  
  167.   gimp_install_procedure ("plug_in_gauss_iir2",
  168.               "Applies a gaussian blur to the specified drawable.",
  169.               "Applies a gaussian blur to the drawable, with "
  170.               "specified radius of affect.  The standard deviation "
  171.               "of the normal distribution used to modify pixel "
  172.               "values is calculated based on the supplied radius.  "
  173.               "This radius can be specified indepently on for the "
  174.               "horizontal and the vertical direction. The IIR "
  175.               "gaussian blurring works best for large radius "
  176.               "values and for images which are not "
  177.               "computer-generated.  Values for radii less than "
  178.               "1.0 would generate spurious results. Therefore "
  179.               "they are interpreted as 0.0, which means that the "
  180.               "computation for this orientation is skipped.",
  181.               "Spencer Kimball, Peter Mattis & Sven Neumann",
  182.               "Spencer Kimball, Peter Mattis & Sven Neumann",
  183.               "1995-2000",
  184.               N_("<Image>/Filters/Blur/Gaussian Blur (IIR)..."),
  185.               "RGB*, GRAY*",
  186.               GIMP_PLUGIN,
  187.               nargs2, 0,
  188.               args2, NULL);
  189. }
  190.  
  191. static void
  192. run (gchar      *name,
  193.      gint        nparams,
  194.      GimpParam  *param,
  195.      gint       *nreturn_vals,
  196.      GimpParam **return_vals)
  197. {
  198.   static GimpParam values[1];
  199.   gint32 image_ID;
  200.   GimpDrawable *drawable;
  201.   GimpRunModeType run_mode;
  202.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  203.  
  204.   run_mode = param[0].data.d_int32;
  205.  
  206.   *nreturn_vals = 1;
  207.   *return_vals = values;
  208.  
  209.   values[0].type = GIMP_PDB_STATUS;
  210.   values[0].data.d_status = status;
  211.  
  212.   /*  Get the specified image and drawable  */
  213.   image_ID = param[1].data.d_image;
  214.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  215.  
  216.   if (strcmp (name, "plug_in_gauss_iir") == 0)   /* the old-fashioned way of calling it */
  217.     {
  218.       switch (run_mode)
  219.     {
  220.     case GIMP_RUN_INTERACTIVE:
  221.       INIT_I18N_UI();
  222.       /*  Possibly retrieve data  */
  223.       gimp_get_data ("plug_in_gauss_iir", &bvals);
  224.       
  225.       /*  First acquire information with a dialog  */
  226.       if (! gauss_iir_dialog ())
  227.         return;
  228.       break;
  229.       
  230.     case GIMP_RUN_NONINTERACTIVE:
  231.       /*  Make sure all the arguments are there!  */
  232.       if (nparams != 6)
  233.         status = GIMP_PDB_CALLING_ERROR;
  234.       if (status == GIMP_PDB_SUCCESS)
  235.         {
  236.           bvals.radius     = param[3].data.d_float;
  237.           bvals.horizontal = (param[4].data.d_int32) ? TRUE : FALSE;
  238.           bvals.vertical   = (param[5].data.d_int32) ? TRUE : FALSE;
  239.         }
  240.       if (status == GIMP_PDB_SUCCESS && (bvals.radius < 1.0))
  241.         status = GIMP_PDB_CALLING_ERROR;
  242.       INIT_I18N();
  243.       break;
  244.       
  245.     case GIMP_RUN_WITH_LAST_VALS:
  246.       INIT_I18N();
  247.       /*  Possibly retrieve data  */
  248.       gimp_get_data ("plug_in_gauss_iir", &bvals);
  249.       break;
  250.       
  251.     default:
  252.       break;
  253.     }
  254.  
  255.       if (!(bvals.horizontal || bvals.vertical))
  256.     {
  257.       gimp_message ( _("gauss_iir: you must specify either horizontal or vertical (or both)"));
  258.       status = GIMP_PDB_CALLING_ERROR;
  259.     }
  260.     }
  261.   else if (strcmp (name, "plug_in_gauss_iir2") == 0)
  262.     {
  263.       switch (run_mode)
  264.     {      
  265.     case GIMP_RUN_INTERACTIVE:
  266.       INIT_I18N_UI();
  267.       /*  Possibly retrieve data  */
  268.       gimp_get_data ("plug_in_gauss_iir2", &b2vals);
  269.       
  270.       /*  First acquire information with a dialog  */
  271.       if (! gauss_iir2_dialog (image_ID, drawable))
  272.         return;
  273.       break;
  274.     case GIMP_RUN_NONINTERACTIVE:
  275.       INIT_I18N();
  276.       /*  Make sure all the arguments are there!  */
  277.       if (nparams != 5)
  278.         status = GIMP_PDB_CALLING_ERROR;
  279.       if (status == GIMP_PDB_SUCCESS)
  280.         {
  281.           b2vals.horizontal = param[3].data.d_float;
  282.           b2vals.vertical   = param[4].data.d_float;
  283.         }
  284.       if (status == GIMP_PDB_SUCCESS && (b2vals.horizontal < 1.0 && b2vals.vertical < 1.0))
  285.         status = GIMP_PDB_CALLING_ERROR;
  286.       break;
  287.       
  288.     case GIMP_RUN_WITH_LAST_VALS:
  289.       INIT_I18N();
  290.       /*  Possibly retrieve data  */
  291.       gimp_get_data ("plug_in_gauss_iir2", &b2vals);
  292.       break;
  293.       
  294.     default:
  295.       break;
  296.     }
  297.     }
  298.   else
  299.     status = GIMP_PDB_CALLING_ERROR;
  300.  
  301.   if (status == GIMP_PDB_SUCCESS)
  302.     {
  303.       /*  Make sure that the drawable is gray or RGB color  */
  304.       if (gimp_drawable_is_rgb (drawable->id) ||
  305.       gimp_drawable_is_gray (drawable->id))
  306.     {
  307.       gimp_progress_init ( _("IIR Gaussian Blur"));
  308.       
  309.           /*  set the tile cache size so that the gaussian blur works well  */
  310.           gimp_tile_cache_ntiles (2 * (MAX (drawable->width, drawable->height) /
  311.                   gimp_tile_width () + 1));
  312.  
  313.           /*  run the gaussian blur  */
  314.       if (strcmp (name, "plug_in_gauss_iir") == 0)
  315.         {
  316.           gauss_iir (drawable, (bvals.horizontal ? bvals.radius : 0.0), 
  317.                                    (bvals.vertical ? bvals.radius : 0.0));
  318.           
  319.           /*  Store data  */
  320.           if (run_mode == GIMP_RUN_INTERACTIVE)
  321.         gimp_set_data ("plug_in_gauss_iir", &bvals, sizeof (BlurValues));
  322.         } 
  323.       else
  324.         {
  325.           gauss_iir (drawable, b2vals.horizontal, b2vals.vertical);
  326.       
  327.           /*  Store data  */
  328.           if (run_mode == GIMP_RUN_INTERACTIVE)
  329.         gimp_set_data ("plug_in_gauss_iir2", &b2vals, sizeof (Blur2Values));
  330.         }
  331.  
  332.           if (run_mode != GIMP_RUN_NONINTERACTIVE)
  333.         gimp_displays_flush ();
  334.         }
  335.       else
  336.         {
  337.           gimp_message ( "gauss_iir: cannot operate on indexed color images");
  338.           status = GIMP_PDB_EXECUTION_ERROR;
  339.         }
  340.  
  341.       gimp_drawable_detach (drawable);
  342.     }
  343.  
  344.   values[0].data.d_status = status;
  345. }
  346.  
  347. static gint
  348. gauss_iir_dialog (void)
  349. {
  350.   GtkWidget *dlg;
  351.   GtkWidget *label;
  352.   GtkWidget *spinbutton;
  353.   GtkObject *adj;
  354.   GtkWidget *toggle;
  355.   GtkWidget *frame;
  356.   GtkWidget *vbox;
  357.   GtkWidget *hbox;
  358.  
  359.   gimp_ui_init ("gauss_iir", FALSE);
  360.  
  361.   dlg = gimp_dialog_new (_("IIR Gaussian Blur"), "gauss_iir",
  362.              gimp_standard_help_func, "filters/gauss_iir.html",
  363.              GTK_WIN_POS_MOUSE,
  364.              FALSE, TRUE, FALSE,
  365.  
  366.              _("OK"), gauss_ok_callback,
  367.              NULL, NULL, NULL, TRUE, FALSE,
  368.              _("Cancel"), gtk_widget_destroy,
  369.              NULL, 1, NULL, FALSE, TRUE,
  370.  
  371.              NULL);
  372.  
  373.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  374.               GTK_SIGNAL_FUNC (gtk_main_quit),
  375.               NULL);
  376.  
  377.   /*  parameter settings  */
  378.   frame = gtk_frame_new (_("Parameter Settings"));
  379.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  380.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  381.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  382.  
  383.   vbox = gtk_vbox_new (FALSE, 2);
  384.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  385.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  386.  
  387.   toggle = gtk_check_button_new_with_label (_("Blur Horizontally"));
  388.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  389.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  390.               (GtkSignalFunc) gimp_toggle_button_update,
  391.               &bvals.horizontal);
  392.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.horizontal);
  393.   gtk_widget_show (toggle);
  394.  
  395.   toggle = gtk_check_button_new_with_label (_("Blur Vertically"));
  396.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  397.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  398.               (GtkSignalFunc) gimp_toggle_button_update,
  399.               &bvals.vertical);
  400.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.vertical);
  401.   gtk_widget_show (toggle);
  402.  
  403.   hbox = gtk_hbox_new (FALSE, 4);
  404.   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  405.  
  406.   label = gtk_label_new (_("Blur Radius:"));
  407.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  408.   gtk_widget_show (label);
  409.  
  410.   spinbutton = gimp_spin_button_new (&adj,
  411.                      bvals.radius, 1.0, GIMP_MAX_IMAGE_SIZE,
  412.                      1.0, 5.0, 0, 1, 2);
  413.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, TRUE, TRUE, 0);
  414.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  415.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  416.               &bvals.radius);
  417.   gtk_widget_show (spinbutton);
  418.  
  419.   gtk_widget_show (hbox);
  420.   gtk_widget_show (vbox);
  421.   gtk_widget_show (frame);
  422.   gtk_widget_show (dlg);
  423.  
  424.   gtk_main ();
  425.   gdk_flush ();
  426.  
  427.   return bint.run;
  428. }
  429.  
  430.  
  431. static gint
  432. gauss_iir2_dialog (gint32        image_ID,
  433.            GimpDrawable *drawable)
  434. {
  435.   GtkWidget *dlg;
  436.   GtkWidget *frame;
  437.   GtkWidget *size;
  438.   GimpUnit   unit;
  439.   gdouble    xres;
  440.   gdouble    yres;
  441.  
  442.   gimp_ui_init ("gauss_iir2", FALSE);
  443.  
  444.   dlg = gimp_dialog_new (_("IIR Gaussian Blur"), "gauss_iir",
  445.              gimp_standard_help_func, "filters/gauss_iir.html",
  446.              GTK_WIN_POS_MOUSE,
  447.              FALSE, TRUE, FALSE,
  448.  
  449.              _("OK"), gauss_ok_callback,
  450.              NULL, NULL, NULL, TRUE, FALSE,
  451.              _("Cancel"), gtk_widget_destroy,
  452.              NULL, 1, NULL, FALSE, TRUE,
  453.  
  454.              NULL);
  455.  
  456.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  457.               GTK_SIGNAL_FUNC (gtk_main_quit),
  458.               NULL);
  459.  
  460.   /*  parameter settings  */
  461.   frame = gtk_frame_new (_("Blur Radius"));
  462.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  463.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  464.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  465.  
  466.   /*  Get the image resolution and unit  */
  467.   gimp_image_get_resolution (image_ID, &xres, &yres);
  468.   unit = gimp_image_get_unit (image_ID);
  469.  
  470.   size = gimp_coordinates_new (unit, "%a", TRUE, FALSE, 75, 
  471.                    GIMP_SIZE_ENTRY_UPDATE_SIZE,
  472.  
  473.                    b2vals.horizontal == b2vals.vertical,
  474.                    FALSE,
  475.  
  476.                    _("Horizontal:"), b2vals.horizontal, xres,
  477.                    0, 8 * MAX (drawable->width, drawable->height),
  478.                    0, 0,
  479.  
  480.                    _("Vertical:"), b2vals.vertical, yres,
  481.                    0, 8 * MAX (drawable->width, drawable->height),
  482.                    0, 0);
  483.   gtk_container_set_border_width (GTK_CONTAINER (size), 4);
  484.   gtk_container_add (GTK_CONTAINER (frame), size);
  485.  
  486.   gtk_widget_show (size);
  487.   gtk_widget_show (frame);
  488.   gtk_widget_show (dlg);
  489.  
  490.   bint.size = size;
  491.  
  492.   gtk_main ();
  493.   gdk_flush ();
  494.  
  495.   return bint.run;
  496. }
  497.  
  498. static void
  499. gauss_ok_callback (GtkWidget *widget,
  500.            gpointer   data)
  501. {
  502.   b2vals.horizontal =
  503.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (bint.size), 0);
  504.   b2vals.vertical =
  505.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (bint.size), 1);
  506.  
  507.   bint.run = TRUE;
  508.  
  509.   gtk_widget_destroy (GTK_WIDGET (data));
  510. }
  511.  
  512.  
  513. /* Convert from separated to premultiplied alpha, on a single scan line. */
  514. static void
  515. multiply_alpha (guchar *buf,
  516.         gint    width,
  517.         gint    bytes)
  518. {
  519.   gint i, j;
  520.   gdouble alpha;
  521.  
  522.   for (i = 0; i < width * bytes; i += bytes)
  523.     {
  524.       alpha = buf[i + bytes - 1] * (1.0 / 255.0);
  525.       for (j = 0; j < bytes - 1; j++)
  526.     buf[i + j] *= alpha;
  527.     }
  528. }
  529.  
  530. /* Convert from premultiplied to separated alpha, on a single scan
  531.    line. */
  532. static void
  533. separate_alpha (guchar *buf,
  534.         gint    width,
  535.         gint    bytes)
  536. {
  537.   gint i, j;
  538.   guchar alpha;
  539.   gdouble recip_alpha;
  540.   gint new_val;
  541.  
  542.   for (i = 0; i < width * bytes; i += bytes)
  543.     {
  544.       alpha = buf[i + bytes - 1];
  545.       if (alpha != 0 && alpha != 255)
  546.     {
  547.       recip_alpha = 255.0 / alpha;
  548.       for (j = 0; j < bytes - 1; j++)
  549.         {
  550.           new_val = buf[i + j] * recip_alpha;
  551.           buf[i + j] = MIN (255, new_val);
  552.         }
  553.     }
  554.     }
  555. }
  556.  
  557. static void
  558. gauss_iir (GimpDrawable *drawable,
  559.        gdouble       horz,
  560.        gdouble       vert)
  561. {
  562.   GimpPixelRgn src_rgn, dest_rgn;
  563.   gint width, height;
  564.   gint bytes;
  565.   gint has_alpha;
  566.   guchar *dest;
  567.   guchar *src, *sp_p, *sp_m;
  568.   gdouble n_p[5], n_m[5];
  569.   gdouble d_p[5], d_m[5];
  570.   gdouble bd_p[5], bd_m[5];
  571.   gdouble *val_p, *val_m, *vp, *vm;
  572.   gint x1, y1, x2, y2;
  573.   gint i, j;
  574.   gint row, col, b;
  575.   gint terms;
  576.   gdouble progress, max_progress;
  577.   gint initial_p[4];
  578.   gint initial_m[4];
  579.   guchar *guc_tmp1, *guc_tmp2;
  580.   gint *gi_tmp1, *gi_tmp2;
  581.   gdouble std_dev;
  582.  
  583.   if (horz < 1.0 && vert < 1.0)
  584.     return;
  585.  
  586.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  587.  
  588.   width  = (x2 - x1);
  589.   height = (y2 - y1);
  590.  
  591.   if (width < 1 || height < 1)
  592.     return;
  593.  
  594.   bytes = drawable->bpp;
  595.   has_alpha = gimp_drawable_has_alpha(drawable->id);
  596.  
  597.   val_p = g_new (gdouble, MAX (width, height) * bytes);
  598.   val_m = g_new (gdouble, MAX (width, height) * bytes);
  599.  
  600.   src =  g_new (guchar, MAX (width, height) * bytes);
  601.   dest = g_new (guchar, MAX (width, height) * bytes);
  602.  
  603.   gimp_pixel_rgn_init (&src_rgn,
  604.                        drawable, 0, 0, drawable->width, drawable->height,
  605.                        FALSE, FALSE);
  606.   gimp_pixel_rgn_init (&dest_rgn,
  607.                        drawable, 0, 0, drawable->width, drawable->height,
  608.                        TRUE, TRUE);
  609.  
  610.   progress = 0.0;
  611.   max_progress  = (horz < 1.0 ) ? 0 : width * height * horz;
  612.   max_progress += (vert < 1.0 ) ? 0 : width * height * vert;
  613.  
  614.   if (has_alpha)
  615.     multiply_alpha (src, height, bytes);
  616.   
  617.   /*  First the vertical pass  */
  618.   if (vert >= 1.0)
  619.     {
  620.       vert = fabs (vert) + 1.0;
  621.       std_dev = sqrt (-(vert * vert) / (2 * log (1.0 / 255.0)));
  622.  
  623.       /*  derive the constants for calculating the gaussian from the std dev  */
  624.       find_constants (n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev);
  625.  
  626.       for (col = 0; col < width; col++)
  627.     {
  628.       memset(val_p, 0, height * bytes * sizeof (gdouble));
  629.       memset(val_m, 0, height * bytes * sizeof (gdouble));
  630.  
  631.       gimp_pixel_rgn_get_col (&src_rgn, src, col + x1, y1, (y2 - y1));
  632.  
  633.       sp_p = src;
  634.       sp_m = src + (height - 1) * bytes;
  635.       vp = val_p;
  636.       vm = val_m + (height - 1) * bytes;
  637.  
  638.       /*  Set up the first vals  */
  639. #ifndef ORIGINAL_READABLE_CODE
  640.       for(guc_tmp1 = sp_p, guc_tmp2 = sp_m,
  641.         gi_tmp1 = initial_p, gi_tmp2 = initial_m;
  642.           (guc_tmp1 - sp_p) < bytes;)
  643.         {
  644.           *gi_tmp1++ = *guc_tmp1++;
  645.           *gi_tmp2++ = *guc_tmp2++;
  646.         }
  647. #else
  648.       for (i = 0; i < bytes; i++)
  649.         {
  650.           initial_p[i] = sp_p[i];
  651.           initial_m[i] = sp_m[i];
  652.         }
  653. #endif
  654.  
  655.       for (row = 0; row < height; row++)
  656.         {
  657.           gdouble *vpptr, *vmptr;
  658.           terms = (row < 4) ? row : 4;
  659.  
  660.           for (b = 0; b < bytes; b++)
  661.         {
  662.           vpptr = vp + b; vmptr = vm + b;
  663.           for (i = 0; i <= terms; i++)
  664.             {
  665.               *vpptr += n_p[i] * sp_p[(-i * bytes) + b] -
  666.             d_p[i] * vp[(-i * bytes) + b];
  667.               *vmptr += n_m[i] * sp_m[(i * bytes) + b] -
  668.             d_m[i] * vm[(i * bytes) + b];
  669.             }
  670.           for (j = i; j <= 4; j++)
  671.             {
  672.               *vpptr += (n_p[j] - bd_p[j]) * initial_p[b];
  673.               *vmptr += (n_m[j] - bd_m[j]) * initial_m[b];
  674.             }
  675.         }
  676.  
  677.           sp_p += bytes;
  678.           sp_m -= bytes;
  679.           vp += bytes;
  680.           vm -= bytes;
  681.         }
  682.  
  683.       transfer_pixels (val_p, val_m, dest, bytes, height);
  684.  
  685.       gimp_pixel_rgn_set_col (&dest_rgn, dest, col + x1, y1, (y2 - y1));
  686.  
  687.       progress += height * vert;
  688.       if ((col % 5) == 0)
  689.         gimp_progress_update (progress / max_progress);
  690.     }
  691.  
  692.       /*  prepare for the horizontal pass  */
  693.       gimp_pixel_rgn_init (&src_rgn,
  694.                            drawable, 0, 0, drawable->width, drawable->height,
  695.                            FALSE, TRUE);
  696.     }
  697.  
  698.   /*  Now the horizontal pass  */
  699.   if (horz >= 1.0)
  700.     {
  701.       horz = fabs (horz) + 1.0;
  702.  
  703.       if (horz != vert)
  704.     {
  705.       std_dev = sqrt (-(horz * horz) / (2 * log (1.0 / 255.0)));
  706.             
  707.       /*  derive the constants for calculating the gaussian from the std dev  */
  708.       find_constants (n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev);
  709.     }
  710.  
  711.       for (row = 0; row < height; row++)
  712.     {
  713.       memset(val_p, 0, width * bytes * sizeof (gdouble));
  714.       memset(val_m, 0, width * bytes * sizeof (gdouble));
  715.  
  716.       gimp_pixel_rgn_get_row (&src_rgn, src, x1, row + y1, (x2 - x1));
  717.  
  718.       sp_p = src;
  719.       sp_m = src + (width - 1) * bytes;
  720.       vp = val_p;
  721.       vm = val_m + (width - 1) * bytes;
  722.  
  723.       /*  Set up the first vals  */
  724. #ifndef ORIGINAL_READABLE_CODE
  725.       for(guc_tmp1 = sp_p, guc_tmp2 = sp_m,
  726.         gi_tmp1 = initial_p, gi_tmp2 = initial_m;
  727.           (guc_tmp1 - sp_p) < bytes;)
  728.         {
  729.           *gi_tmp1++ = *guc_tmp1++;
  730.           *gi_tmp2++ = *guc_tmp2++;
  731.         }
  732. #else
  733.       for (i = 0; i < bytes; i++)
  734.         {
  735.           initial_p[i] = sp_p[i];
  736.           initial_m[i] = sp_m[i];
  737.         }
  738. #endif
  739.  
  740.       for (col = 0; col < width; col++)
  741.         {
  742.           gdouble *vpptr, *vmptr;
  743.           terms = (col < 4) ? col : 4;
  744.  
  745.           for (b = 0; b < bytes; b++)
  746.         {
  747.           vpptr = vp + b; vmptr = vm + b;
  748.           for (i = 0; i <= terms; i++)
  749.             {
  750.               *vpptr += n_p[i] * sp_p[(-i * bytes) + b] -
  751.             d_p[i] * vp[(-i * bytes) + b];
  752.               *vmptr += n_m[i] * sp_m[(i * bytes) + b] -
  753.             d_m[i] * vm[(i * bytes) + b];
  754.             }
  755.           for (j = i; j <= 4; j++)
  756.             {
  757.               *vpptr += (n_p[j] - bd_p[j]) * initial_p[b];
  758.               *vmptr += (n_m[j] - bd_m[j]) * initial_m[b];
  759.             }
  760.         }
  761.  
  762.           sp_p += bytes;
  763.           sp_m -= bytes;
  764.           vp += bytes;
  765.           vm -= bytes;
  766.         }
  767.  
  768.       transfer_pixels (val_p, val_m, dest, bytes, width);
  769.  
  770.       gimp_pixel_rgn_set_row (&dest_rgn, dest, x1, row + y1, (x2 - x1));
  771.  
  772.       progress += width * horz;
  773.       if ((row % 5) == 0)
  774.         gimp_progress_update (progress / max_progress);
  775.     }
  776.     }
  777.  
  778.   if (has_alpha)
  779.     separate_alpha (dest, width, bytes);
  780.  
  781.   /*  merge the shadow, update the drawable  */
  782.   gimp_drawable_flush (drawable);
  783.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  784.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  785.  
  786.   /*  free up buffers  */
  787.   g_free (val_p);
  788.   g_free (val_m);
  789.   g_free (src);
  790.   g_free (dest);
  791. }
  792.  
  793. static void
  794. transfer_pixels (gdouble *src1,
  795.          gdouble *src2,
  796.          guchar  *dest,
  797.          gint     bytes,
  798.          gint     width)
  799. {
  800.   gint b;
  801.   gint bend = bytes * width;
  802.   gdouble sum;
  803.  
  804.   for(b = 0; b < bend; b++)
  805.     {
  806.       sum = *src1++ + *src2++;
  807.       if (sum > 255) sum = 255;
  808.       else if(sum < 0) sum = 0;
  809.       
  810.       *dest++ = (guchar) sum;
  811.     }
  812. }
  813.  
  814. static void
  815. find_constants (gdouble n_p[],
  816.         gdouble n_m[],
  817.         gdouble d_p[],
  818.         gdouble d_m[],
  819.         gdouble bd_p[],
  820.         gdouble bd_m[],
  821.         gdouble std_dev)
  822. {
  823.   gint i;
  824.   gdouble constants [8];
  825.   gdouble div;
  826.  
  827.   /*  The constants used in the implemenation of a casual sequence
  828.    *  using a 4th order approximation of the gaussian operator
  829.    */
  830.  
  831.   div = sqrt(2 * G_PI) * std_dev;
  832.   constants [0] = -1.783 / std_dev;
  833.   constants [1] = -1.723 / std_dev;
  834.   constants [2] = 0.6318 / std_dev;
  835.   constants [3] = 1.997  / std_dev;
  836.   constants [4] = 1.6803 / div;
  837.   constants [5] = 3.735 / div;
  838.   constants [6] = -0.6803 / div;
  839.   constants [7] = -0.2598 / div;
  840.  
  841.   n_p [0] = constants[4] + constants[6];
  842.   n_p [1] = exp (constants[1]) *
  843.     (constants[7] * sin (constants[3]) -
  844.      (constants[6] + 2 * constants[4]) * cos (constants[3])) +
  845.        exp (constants[0]) *
  846.      (constants[5] * sin (constants[2]) -
  847.       (2 * constants[6] + constants[4]) * cos (constants[2]));
  848.   n_p [2] = 2 * exp (constants[0] + constants[1]) *
  849.     ((constants[4] + constants[6]) * cos (constants[3]) * cos (constants[2]) -
  850.      constants[5] * cos (constants[3]) * sin (constants[2]) -
  851.      constants[7] * cos (constants[2]) * sin (constants[3])) +
  852.        constants[6] * exp (2 * constants[0]) +
  853.      constants[4] * exp (2 * constants[1]);
  854.   n_p [3] = exp (constants[1] + 2 * constants[0]) *
  855.     (constants[7] * sin (constants[3]) - constants[6] * cos (constants[3])) +
  856.       exp (constants[0] + 2 * constants[1]) *
  857.     (constants[5] * sin (constants[2]) - constants[4] * cos (constants[2]));
  858.   n_p [4] = 0.0;
  859.  
  860.   d_p [0] = 0.0;
  861.   d_p [1] = -2 * exp (constants[1]) * cos (constants[3]) -
  862.     2 * exp (constants[0]) * cos (constants[2]);
  863.   d_p [2] = 4 * cos (constants[3]) * cos (constants[2]) * exp (constants[0] + constants[1]) +
  864.     exp (2 * constants[1]) + exp (2 * constants[0]);
  865.   d_p [3] = -2 * cos (constants[2]) * exp (constants[0] + 2 * constants[1]) -
  866.     2 * cos (constants[3]) * exp (constants[1] + 2 * constants[0]);
  867.   d_p [4] = exp (2 * constants[0] + 2 * constants[1]);
  868.  
  869. #ifndef ORIGINAL_READABLE_CODE
  870.   memcpy(d_m, d_p, 5 * sizeof(gdouble));
  871. #else
  872.   for (i = 0; i <= 4; i++)
  873.     d_m [i] = d_p [i];
  874. #endif
  875.  
  876.   n_m[0] = 0.0;
  877.   for (i = 1; i <= 4; i++)
  878.     n_m [i] = n_p[i] - d_p[i] * n_p[0];
  879.  
  880.   {
  881.     gdouble sum_n_p, sum_n_m, sum_d;
  882.     gdouble a, b;
  883.  
  884.     sum_n_p = 0.0;
  885.     sum_n_m = 0.0;
  886.     sum_d = 0.0;
  887.     for (i = 0; i <= 4; i++)
  888.       {
  889.     sum_n_p += n_p[i];
  890.     sum_n_m += n_m[i];
  891.     sum_d += d_p[i];
  892.       }
  893.  
  894. #ifndef ORIGINAL_READABLE_CODE
  895.     sum_d++;
  896.     a = sum_n_p / sum_d;
  897.     b = sum_n_m / sum_d;
  898. #else
  899.     a = sum_n_p / (1 + sum_d);
  900.     b = sum_n_m / (1 + sum_d);
  901. #endif
  902.  
  903.     for (i = 0; i <= 4; i++)
  904.       {
  905.     bd_p[i] = d_p[i] * a;
  906.     bd_m[i] = d_m[i] * b;
  907.       }
  908.   }
  909. }
  910.